import { ChangeDetectorRef, Component, HostListener, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { editor } from 'monaco-editor';
import { NzMessageService } from 'ng-zorro-antd/message';
import { NzResizeEvent } from 'ng-zorro-antd/resizable';
import { filter, first, map, tap } from 'rxjs/operators';
import { PyodideService } from '../pyodide/pyodide.service';
import { NzConfigService } from 'ng-zorro-antd/core/config';
// eslint-disable-next-line  @typescript-eslint/no-explicit-any
declare const monaco: any;
import nightowl from "./NightOwl";
import { Course, CoursePage } from './Course';
import { from, Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { CourseLoaderService } from './course-loader.service';
import { PythonExceptionMessage, PythonInputMessage, PythonOutputMessage } from '../pyodide/PythonMessage';
import { DBService } from '../db/db.service';
import { Collection, Table } from 'dexie';
import { MarkdownService } from "ngx-markdown";
import { Title } from '@angular/platform-browser';

@Component({ selector: 'app-course', templateUrl: './course.component.html', styleUrls: ['./course.component.scss'] })
export class CourseComponent implements OnInit {
    id: string = '';
    pyodideLoaded = false;
    constructor(private route: ActivatedRoute, private pyodide: PyodideService, private message: NzMessageService, private courseLoader: CourseLoaderService, private window: Window, private cdr: ChangeDetectorRef, private router: Router, private dbService: DBService, private markdownService: MarkdownService, private title: Title) { }
    ngOnInit(): void {
        this.id = this.route.snapshot.paramMap.get('id') as string;
        this.codeTable = this.dbService.table('code');
        this.courseProgressTable = this.dbService.table('courseProgress');
        const linkRenderer = this.markdownService.renderer.link;
        this.markdownService.renderer.link = (href, title, text) => {
            const html = linkRenderer.call(this.markdownService.renderer, href, title, text);
            return html.replace(/^<a /, '<a target="_blank" rel="nofollow" ');
        };
        from(this.courseLoader.get(this.id)).subscribe(course=>{
            this.course = course;
            if (this.course && this.course.page.length > 0) {
                this.title.setTitle(this.course.name);
                this.loadPage(this.course.page[0]);
            }
            this.pyodide.loadingState.asObservable().pipe(filter(x => x === 'loaded'), first()).subscribe(x => {
                this.pyodideLoaded = true;
            });
            from(this.courseProgressTable!.where('id').equals(this.id).toArray()).subscribe(result => {
                if (result.length === 0) { } else {
                    this.maxPageProgress = result[0].page;
                    this.maxPageTaskProgress = result[0].task;
                    if (this.currentPageIndex === this.maxPageProgress) {
                        if (this.maxPageTaskProgress >= this.currentTaskIndex) {
                            this.currentTaskIndex = this.maxPageTaskProgress;
                            this.tasks.forEach((v, i) => {
                                if (i <= this.maxPageTaskProgress) {
                                    v.done = true;
                                    this.currentTaskIndex = this.tasks.length - 1;
                                }
                            })
                        }
                    } else if (this.currentPageIndex < this.maxPageProgress) {
                        this.tasks.forEach(v => v.done = true);
                    }
                }
            }
            );
            // this.pyodide.redirect((text) => prompt(text)as string, (text) => this.output = text);
        })
    }
    course?: Course;
    currentPageIndex = 0;
    currentTaskIndex = 0;
    maxPageProgress = 0;
    maxPageTaskProgress = -1;
    tasks: {
        title: string,
        description: string,
        done: boolean
    }[] = [];
    // 运行对话框，在代码运行时实现
    isRunModalVisible = false;
    courseProgressTable?: Table;
    markdown: string = '';
    loadPage(page: CoursePage) {
        this.currentTaskIndex = 0;
        this.loadCode(page.code, page.preinput);
        this.markdown = page.markdown;
        // console.log(this.markdown);
        this.tasks = page.task.map(x => ({ title: x.title, description: x.description, done: false }));
        if (this.currentPageIndex === this.maxPageProgress) {
            if (this.maxPageTaskProgress >= this.currentTaskIndex) {
                this.currentTaskIndex = this.maxPageTaskProgress;
                this.tasks.forEach((v, i) => {
                    if (i <= this.maxPageTaskProgress) {
                        v.done = true;
                    }
                })
            }
        } else if (this.currentPageIndex < this.maxPageProgress) {
            this.tasks.forEach(v => v.done = true);
            this.currentTaskIndex = this.tasks.length - 1;
        }
        this.cdr.detectChanges();
    }
    onReady() {
        // Markdown加载完之后
        // 似乎也没什么可做的
    }
    col = 10;
    animationId = -1;
    code = '';
    inputText = '';
    output = '';
    editorLoaded = false;
    editor?: editor.IStandaloneCodeEditor; codeTable?: Table;
    loadCode(defaultCode: string, defaultInput: string) {
        from(this.codeTable!.where('[course+page]').equals([
            this.course!.id,
            this.currentPageIndex
        ]).toArray()).subscribe(result => {
            if (result.length === 0) {
                this.code = defaultCode;
                this.inputText = defaultInput;
            } else {
                this.code = result[0].code;
                this.inputText = result[0].input;
            }
            this.cdr.detectChanges();
            // 也许会触发一次saveCode？不过不重要
        });
    }
    saveCode() { // TODO check null
        this.codeTable!.put({
            course: this.course!.id,
            page: this.currentPageIndex,
            code: this.code,
            input: this.inputText
        });
    }
    inputTextChanged() {
        this.saveCode();
    }
    onEditorInit(e: editor.ICodeEditor | editor.IEditor): void { // 这里的转换不太行
        this.editor = e as editor.IStandaloneCodeEditor;
        this.editorLoaded = true;
        // 大概是触发条件
        const blockContext = "editorTextFocus && !suggestWidgetVisible && !renameInputVisible && !inSnippetMode " + "&& !quickFixWidgetVisible";
        this.editor.addAction({
            id: "execute",
            label: "运行和检查",
            keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
            contextMenuGroupId: "2_execution",
            precondition: blockContext,
            run: () => {
                this.run();
            }
        });
        this.editor.addAction({
            id: "test",
            label: "获取提示",
            keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.Enter],
            contextMenuGroupId: "2_execution",
            precondition: blockContext,
            run: () => {
                this.test();
            }
        });
        monaco.editor.defineTheme('nightowl', nightowl);
        monaco.editor.setTheme('nightowl');
        this.editor.getModel()?.onDidChangeContent(event => {
            this.saveCode();
        });
    }
    onResize({ col }: NzResizeEvent): void {
        cancelAnimationFrame(this.animationId);
        this.animationId = requestAnimationFrame(() => {
            this.col = col!;
        });
        this.editor?.layout();
    }
    taskDescriptionWrapper(text: string) {
        if (text === '') {
            return '本任务没有其他说明哦'
        }
        return text;
    }
    codeRunning = false; 
    currentCheckResult = false; 
    taskDonePrompt = ''; 
    runModalOutputStyle = "";
    async run() { // 在Python中能够访问
        this.isRunModalVisible = true;
        // 不检查有概率不能及时跳出来，可能是因为在多线程里面操作？
        this.cdr.detectChanges();
        let inputLines = this.inputText.split('\n');
        this.output = '';
        if (this.inputText === '') {
            inputLines = [];
        }
        (this.window as any).pyodide_input_lines = inputLines;
        // await this.pyodide.runAsync(this.code);
        this.codeRunning = true;
        const observer = await this.pyodide.simpleRun(this.code, inputLines);
        observer.subscribe(message => {
            if (message.type === 'output') {
                const msg = message as PythonOutputMessage;
                this.output += msg.text;
                this.runModalOutputStyle = "";
            } else if (message.type === "exception") {
                const msg = message as PythonExceptionMessage;
                this.output += msg.message;
                this.codeRunning = false;
                this.runModalOutputStyle = "exception";
            } else if (message.type === "finish") {
                this.codeRunning = false;
            }
            this.cdr.detectChanges();
        });
        // TODO 检查course非空
        const task = this.course!.page[this.currentPageIndex].task[this.currentTaskIndex];
        const checkResult = await this.pyodide.check(this.code, task.check);
        // console.log(task);
        // console.log(checkResult);
        this.currentCheckResult = checkResult;
        if (checkResult) {
            this.tasks[this.currentTaskIndex].done = true;
            if (this.currentPageIndex > this.maxPageProgress || (this.currentPageIndex === this.maxPageProgress && this.currentTaskIndex > this.maxPageTaskProgress)) {
                this.maxPageProgress = this.currentPageIndex;
                this.maxPageTaskProgress = this.currentTaskIndex;
                this.courseProgressTable?.put({ id: this.id, page: this.maxPageProgress, task: this.maxPageTaskProgress, state: "learning" });
            }
            if (this.currentTaskIndex + 1 >= this.course!.page[this.currentPageIndex].task.length) {
                if (this.currentPageIndex + 1 >= this.course!.page.length) {
                    this.taskDonePrompt = '🎉已完成本课程所有任务，按下Enter退出课程，按下Esc留在此课程';
                    this.courseProgressTable?.put({ id: this.id, page: this.currentPageIndex, task: this.currentTaskIndex, state: "finish" });
                    this.modalOkExtra = () => {
                        this.currentPageIndex = 0;
                        this.currentTaskIndex = 0;
                        this.router.navigate(['home']);
                    };
                } else {
                    this.taskDonePrompt = '🎈已完成本页所有任务，按下Enter进入下一页，按下Esc留在此页';
                    this.modalOkExtra = () => {
                        this.currentTaskIndex = 0;
                        this.inputText = '';
                        this.currentPageIndex++;
                        this.loadPage(this.course!.page[this.currentPageIndex]);
                    }
                }
            } else {
                this.taskDonePrompt = '🍾已完成一项任务，按下Enter关闭对话框';
                this.currentTaskIndex++;
                this.modalOkExtra = () => { }
            }
        } else {
            this.modalOkExtra = () => { };
        }
        this.cdr.detectChanges();
    }
    modalOkExtra = () => { }; onRunModalOk() {
        this.modalOkExtra();
        this.closeRunModal();
    }
    modalCancelExtra = () => { }; onRunModalCancel() {
        this.modalCancelExtra();
        this.closeRunModal();
    }
    closeRunModal() {
        this.isRunModalVisible = false;
        this.currentCheckResult = false;
        this.cdr.detectChanges();
    }
    closeTestModal(){
        this.isTestModalVisible = false;
        this.cdr.detectChanges();
    }
    isTestModalVisible = false; 
    testText = ''; 
    async test() {
        const task = this.course!.page[this.currentPageIndex].task[this.currentTaskIndex];
        const observer = await this.pyodide.test(this.code, task.test);
        let hasMessage = false;
        this.isTestModalVisible = true;
        this.testText = '';
        this.cdr.detectChanges();
        observer.subscribe(message => {
            if (message.type === 'output') {
                const msg = message as PythonOutputMessage;
                this.testText += msg.text;
                hasMessage = true;
            } else if (message.type === 'finish') {
                if (!hasMessage) {
                    this.testText = '看起来没有什么建议呢~';
                }
            }
            this.cdr.detectChanges();
        });
    }
    toPreviousPage() {
        this.currentPageIndex--;
        this.loadPage(this.course!.page[this.currentPageIndex]);
    }
    forceToNextPage(){
        if(this.currentPageIndex + 1 >= this.course!.page.length){
            this.courseProgressTable?.put({ id: this.id, page: this.currentPageIndex, task: this.currentTaskIndex, state: "finish" });
            this.router.navigate(['home']);
        }
        this.currentPageIndex++;
        this.loadPage(this.course!.page[this.currentPageIndex]);
    }
    @HostListener("document:keydown", ["$event"]) handleKeyboardEvent(event: KeyboardEvent) { // console.log(event);
        const key = event.key;
        if (key === "Enter") {
            if (event.ctrlKey || event.metaKey) {
                if (event.shiftKey) {
                    if (!this.isTestModalVisible) {
                        this.test();
                    }
                } else {
                    if (!this.isRunModalVisible) {
                        this.run();
                    }
                }
                return false;
            }
        }
        return true;
    }
}
